using System;
using System.Collections.Generic;
using Clandestine.Base;

namespace Clandestine.Input.OSX
{
	internal class OSXJoystickDevice : JoystickDevice
	{
		public override event internalButtonEvent ButtonPressed;
		public override event internalButtonEvent ButtonReleased;
	
		// Button numers are all the real button numbers, then -max of the first axes, + axes of the first axes, -max of second axes etc. etc.
		// this refers to IDevice buttons, which includes our axes, if you get me man. (Treating Joystick as just a series of buttons)
		public override int ButtonCount { get { return buttons.Count + (axes.Count * 2); } } 
	
		private List<IntPtr> buttons = new List<IntPtr>();
		private List<IntPtr> axes = new List<IntPtr>();
		
		private bool[] buttonStatuses; // status of all the buttons - including the imaginary axes ones.
		
		public OSXJoystickDevice(IntPtr device) : base()
		{
			// Get buttons and axes!
			uint elementCount = HIDUtilities.HIDCountDeviceElements(device, (uint)HIDUtilities.HIDElementTypeMask.kHIDElementTypeInput);
			IntPtr element = HIDUtilities.HIDGetFirstDeviceElement(device, (uint)HIDUtilities.HIDElementTypeMask.kHIDElementTypeInput);
			for (int i=0; i < elementCount; i++)
			{
				uint elementUsagePage = HIDUtilities.IOHIDElementGetUsagePage(element);
				uint elementUsage = HIDUtilities.IOHIDElementGetUsage(element);
				
				switch ((HIDUtilities.UsagePages)elementUsagePage)
				{
					case HIDUtilities.UsagePages.kHIDPage_Button:
						this.buttons.Add(element); // Is a button
						break;
						
					case HIDUtilities.UsagePages.kHIDPage_GenericDesktop:
					
						// nice switch from thoth
						switch ((HIDUtilities.GenericDesktop)elementUsage)
						{
							case HIDUtilities.GenericDesktop.kHIDUsage_GD_X:
								this.axes.Add(element); // X Axis
								break;
								
							case HIDUtilities.GenericDesktop.kHIDUsage_GD_Y:
								this.axes.Add(element); // Y Axis
								break;
								
							default:
								Log.w("Unsupported element of OSX JoyStick. Ignoring.");
								break;
						}
						break;
						
					default:
						Log.w("Unsupported element of OSX JoyStick. Ignoring."); // thoth has a few more examples of possible axes.
						break;
				}
				
				element = HIDUtilities.HIDGetNextDeviceElement(element, (uint)HIDUtilities.HIDElementTypeMask.kHIDElementTypeInput);
			}
			
			// Setup buttonStatuses!
			this.buttonStatuses = new bool[this.ButtonCount];
		}
		
		public override bool IsButtonPressed(int n)
		{
			if ((n < 0) || (n >= this.ButtonCount))
				throw new ArgumentException();
			return this.buttonStatuses[n];
		}
		
		public override void ProcessEvents()
		{
			bool[] newButtonStatuses = new bool[this.ButtonCount];
		
			// Go through buttons
			// Note: need to use Physical values rather than Calibrated here, cos the latter doesn't work -- and how do you calibrate a button anyway?
			for (int i = 0; i < this.buttons.Count; i++)
				newButtonStatuses[i] = (HIDUtilities.IOHIDElement_GetValue(buttons[i], (uint)HIDUtilities.IOHIDValueScaleType.kIOHIDValueScaleTypePhysical) > 0) ? true : false;
			
			// Axes
			for (int i = 0; i < this.axes.Count; i++)
			{
				double val = HIDUtilities.IOHIDElement_GetValue(axes[i], (uint)HIDUtilities.IOHIDValueScaleType.kIOHIDValueScaleTypePhysical);
				double minVal = HIDUtilities.IOHIDElementGetPhysicalMin(axes[i]);
				double maxVal = HIDUtilities.IOHIDElementGetPhysicalMax(axes[i]);
				double noDirection = (minVal + maxVal) / 2; // the mid-value that means no buttons are pressed. 
				
				// I think this makes sense, if not, you have val, minVal and maxVal -- write it again!
				if ((val < (noDirection - (noDirection*DeadZone))))
				{
					newButtonStatuses[buttons.Count + (i*2)] = true;
					newButtonStatuses[buttons.Count + (i*2) + 1] = false;
				} else if ((val > (noDirection + (noDirection*DeadZone))))
				{
					newButtonStatuses[buttons.Count + (i*2)] = false;
					newButtonStatuses[buttons.Count + (i*2) + 1] = true;
				}
			}
			
			// Go through the new statuses and check vs. the old to see what events need to be raised!
			bool[] oldButtonStatuses = this.buttonStatuses; // so if they do .IsPressed in the event handler it returns the correct value.
			this.buttonStatuses = newButtonStatuses;
			for (int i = 0; i < newButtonStatuses.Length; i++)
				if (newButtonStatuses[i] != oldButtonStatuses[i])
				{
					if (newButtonStatuses[i])
					{
						if (this.ButtonPressed != null)
							ButtonPressed(this, i); 
					}
					else
					{
						if (this.ButtonReleased != null)
							ButtonReleased(this, i);
					}
				}
		}
	}
}

